package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.pattern.factory.StrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Autowired
	private WxMaService wxMaService;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RabbitService rabbitService;

	@Autowired
	private UserPaidAlbumMapper userPaidAlbumMapper;

	@Autowired
	private UserPaidTrackMapper userPaidTrackMapper;

	@Autowired
	private AlbumFeignClient albumFeignClient;

	@Autowired
	private UserVipServiceMapper userVipServiceMapper;

	@Autowired
	private VipServiceConfigMapper vipServiceConfigMapper;

	@Autowired
	private StrategyFactory strategyFactory;


	/**
	 * 微信一键登录
	 *
	 * @param code 小程序端根据当前微信，生成访问为微信服务端临时凭据
	 * @return
	 */
	@Override
	public Map<String, String> wxLogin(String code) {
		try {
		//1.拿着临时凭据+应用ID+应用秘钥 调用微信接口 获取当前微信账户唯一标识：openId
		//1.1 微信账户信息业务类
		WxMaUserService userService = wxMaService.getUserService();
		//1.2 获取会话信息
		WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
		//1.3 获取微信账号唯一标识
		String openid = sessionInfo.getOpenid();

		//2.根据微信账户唯一标识，查询数据库，看当前微信是否已经注册
			UserInfo userInfo = userInfoMapper.selectOne(
					new LambdaQueryWrapper<UserInfo>()
							.eq(UserInfo::getWxOpenId, openid)
			);

			//3.如果微信账户首次注册，则系只能用户记录，为用户初始化账户记录用于后续订单支付
			if (userInfo == null) {
				//3.1 新增用户记录 绑定微信账户唯一标识
				userInfo = new UserInfo();
				userInfo.setWxOpenId(openid);
				userInfo.setNickname(IdUtil.nanoId());

				userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2025-09-25/c6cc0215-f775-410d-a1ef-031c1f9f583e.jpg");
				userInfoMapper.insert(userInfo);
				//3.2 TODO 为当前注册用户初始化账户记录
				// 方案一：Openfeign远程调用 分布式事务问题  方案二：采用MQ可靠性消息队列实现数据最终一致
				//使用的MQ
				//3.2.1 构建消息对象 注意：如果是VO对象一定要实现序列化接口以及生成序列化版本号
				HashMap<String, Object> map = new HashMap<>();
				map.put("userId",userInfo.getId());
				map.put("amount",new BigDecimal("100"));
				map.put("title","新用户专项体验金活动");
				map.put("orderNo","ZS"+IdUtil.getSnowflakeNextId());
				//3.2.2 发送消息到MQ
				rabbitService.sendMessage(MqConst.EXCHANGE_USER,MqConst.ROUTING_USER_REGISTER,map);
			}

			//4.基于当前用户信息生成令牌
			//4.1 创建令牌
			String token = IdUtil.randomUUID();
			//4.2 构建登录成功后Redis的Key  形式为： user:login:token
			String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
			//4.3 构建登录成功后Redis的Value  形式为:userInfo
			UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
			//4.4 存入Redis  设置有效期: 7天
			redisTemplate.opsForValue().set(loginKey,userInfoVo,RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

			//5. 封装令牌返回给前端
			HashMap<String, String> map = new HashMap<>();
			map.put("token",token);
			return map;
		} catch (WxErrorException e) {
			log.error("微信登录失败");
			throw new GuiguException(500,"微信登陆失败");
		}

	}


	/**
	 * 根据用户ID查询用户基本信息
	 *
	 * @param userId
	 * @return
	 */
	@Override
	@GuiGuCache(prefix = "user:")
	public UserInfoVo getUserInfo(Long userId) {
		UserInfo userInfo = userInfoMapper.selectById(userId);
		if (userInfo != null){
			return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
		}
		return null;
	}


	/**
	 * 更新用户信息方法
	 * 只允许修改昵称头像
	 * @param userInfoVo
	 */
	@Override
	public void updateUser(UserInfoVo userInfoVo) {
		//1.获取用户ID
		Long userId = AuthContextHolder.getUserId();
		//2. 更新用户昵称喝头像
		UserInfo userInfo = new UserInfo();
		userInfo.setId(userId);
		userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
		userInfo.setNickname(userInfoVo.getNickname());
		userInfoMapper.updateById(userInfo);
	}


	/**
	 * 提交需要检查购买状态声音ID列表，响应每个声音购买状态
	 *
	 * @param userId                        用户ID
	 * @param albumId                       专辑ID
	 * @param needCheckPayStatusTrackIdList 待检查购买状态声音ID列表
	 * @return
	 */
	@Override
	public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIdList) {
		HashMap<Long, Integer> map = new HashMap<>();
		//1.根据用户ID+专辑ID查询专辑购买记录
		Long count = userPaidAlbumMapper.selectCount(
				new LambdaQueryWrapper<UserPaidAlbum>()
						.eq(UserPaidAlbum::getUserId, userId)
						.eq(UserPaidAlbum::getAlbumId, albumId)
		);

		//2. 如果已购买专辑，将所有待检查购买状态声音 购买状态设置为 1 响应
		if (count > 0) {
			for (Long trackId : needCheckPayStatusTrackIdList) {
				map.put(trackId,1);
			}
			return map;
		}

		//3. 根据用户ID+专辑ID查询已购声音记录
		List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
				new LambdaQueryWrapper<UserPaidTrack>()
						.eq(UserPaidTrack::getUserId, userId)
						.eq(UserPaidTrack::getAlbumId, albumId)
						.select(UserPaidTrack::getTrackId)
		);

		//4. 如果不存再已购声音，将所有待检查购买状态声音 购买状态设置为 0 响应
		if (CollUtil.isEmpty(userPaidTrackList)) {
			for (Long trackId : needCheckPayStatusTrackIdList) {
				map.put(trackId,0);
			}
			return map;
		}

		//5.如果存在已购声音，将提交检查声音ID列表中，已购声购买状态设置为：1。未购买设置为0
		List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
		for (Long trackId : needCheckPayStatusTrackIdList) {
			if (userPaidTrackIdList.contains(trackId)) {
				map.put(trackId,1);
			}else {
				map.put(trackId,0);
			}
		}
		return map;
	}



	/**
	 * 判断当前用户是否购买过指定专辑
	 *
	 * @param albumId
	 * @return
	 */
	@Override
	public Boolean isPaidAlbum(Long userId, Long albumId) {
		Long count = userPaidAlbumMapper.selectCount(
				new LambdaQueryWrapper<UserPaidAlbum>()
						.eq(UserPaidAlbum::getUserId, userId)
						.eq(UserPaidAlbum::getAlbumId, albumId)
		);
		return count > 0;
	}


	/**
	 * 查询当前用户某个专辑下已购声音ID列表
	 *
	 * @param albumId
	 * @return
	 */
	@Override
	public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
		List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
				new LambdaQueryWrapper<UserPaidTrack>()
						.eq(UserPaidTrack::getUserId, userId)
						.eq(UserPaidTrack::getAlbumId, albumId)
						.select(UserPaidTrack::getTrackId)
		);
		if (CollUtil.isNotEmpty(userPaidTrackList)) {
			List<Long> userPaidTrackIdList = userPaidTrackList
					.stream()
					.map(UserPaidTrack::getTrackId)
					.collect(Collectors.toList());
			return userPaidTrackIdList;
		}
		return null;
	}


	/**
	 * 用户支付成功后，虚拟物品发货
	 *
	 * @param userPaidRecordVo
	 * @return
	 */
	@Override
	public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
		//策略模式+工厂模式优化
		//1. 根据商品类型从工厂中返回具体策略实现类对象
		//付款项目类型: 1001-专辑 1002-声音 1003-vip会员
		String itemType = userPaidRecordVo.getItemType();
		DeliveryStrategy deliveryStrategy = strategyFactory.getDeliveryStrategy(itemType);
		//2. 调用策略实现类对象进行虚拟物品发货逻辑
		deliveryStrategy.delivery(userPaidRecordVo);

/*		//1.处理虚拟物品"专辑"发货逻辑
		if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
			//1.1 根据订单编号查询已购专辑记录，如果存在，则返回
			Long count = userPaidAlbumMapper.selectCount(
					new LambdaQueryWrapper<UserPaidAlbum>()
							.eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo())
			);
			if (count > 0) {
				log.info("已存在该订单:{}的已购专辑记录",userPaidRecordVo.getOrderNo());
				return;
			}
			//1.2 如果不存在，则构建专辑已购记录完成保存
			UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
			userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
			userPaidAlbum.setUserId(userPaidRecordVo.getUserId());

			userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
			userPaidAlbumMapper.insert(userPaidAlbum);
		}else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
			//2. 处理虚拟物品"声音"发货逻辑
			//2.1 根据订单编号查询已购声音记录，如果存在，则返回
			Long count = userPaidTrackMapper.selectCount(
					new LambdaQueryWrapper<UserPaidTrack>()
							.eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo())
			);
			if (count > 0) {
				log.info("已存在该订单:{}的已购声音记录",userPaidRecordVo.getOrderNo());
				return;
			}
			//2.2 如果不存在，则构建声音已购记录完成保存
			TrackInfo trackInfo =
					albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
			List<Long> trackIdList = userPaidRecordVo.getItemIdList();
			for (Long trackId : trackIdList) {
				UserPaidTrack userPaidTrack = new UserPaidTrack();
				userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
				userPaidTrack.setUserId(userPaidRecordVo.getUserId());
				userPaidTrack.setAlbumId(trackInfo.getAlbumId());
				userPaidTrack.setTrackId(trackId);
				userPaidTrackMapper.insert(userPaidTrack);
			}
		}else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
			//3. 处理虚拟物品"会员"发货逻辑
			//3.1 根据订单编号查询已购会员记录，如果存在，则返回
			Long count = userVipServiceMapper.selectCount(
					new LambdaQueryWrapper<UserVipService>()
							.eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo())
			);
			if (count > 0) {
				return;
			}
			//3.2 判断当前用户是否为会员
			Boolean isVIP = false;
			UserInfoVo userInfoVo = this.getUserInfo(userPaidRecordVo.getUserId());
			Date now = new Date();
			if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(now)) {
				isVIP = true;
			}
			//3.3 新增会员购买记录
			UserVipService userVipService = new UserVipService();
			userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
			userVipService.setUserId(userPaidRecordVo.getUserId());
			//3.3.1 获取会员套餐信息得到服务月数
			VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
			Integer serviceMonth = vipServiceConfig.getServiceMonth();
			//3.3.2 计算本次会员购买记录生效时间
			//3.3.3 计算本次会员购买记录失效时间
			if (!isVIP) {
				//普通用户，生效时间为当前时间 失效时间=当前时间+购买会员套餐月数
				userVipService.setStartTime(now);
				userVipService.setExpireTime(DateUtil.offsetMonth(now, serviceMonth));
			}else {
				//会员用户，生效时间为=会员的到期时间+1 失效时间=会员的到期时间+购买会员套餐月数
				DateTime startTime = DateUtil.offsetDay(userInfoVo.getVipExpireTime(), 1);
				userVipService.setStartTime(startTime);
				userVipService.setExpireTime(DateUtil.offsetMonth(startTime, serviceMonth));
			}
			userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
			userVipService.setUserId(userPaidRecordVo.getUserId());
			//3.3.4 新增会员购买记录
			userVipServiceMapper.insert(userVipService);


			//3.4 更新用户会员标识以及更新会员过期时间
			UserInfo userInfo = new UserInfo();
			userInfo.setId(userInfoVo.getId());
			userInfoVo.setIsVip(1);
			userInfoVo.setVipExpireTime(userVipService.getExpireTime());
			userInfoMapper.updateById(userInfo);
		}*/
	}


	/**
	 * 更新VIP状态：处理过期会员
	 *
	 * @return
	 */
	@Override
	public void updateVipExpireStatus(Date date) {
		//1.查询所有VIP会员
		LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(UserInfo::getIsVip,1);
		List<UserInfo> userInfoList = this.list(queryWrapper);

		//2.判断会员是否过期，如果过期将会员标识修改为0
		if (CollectionUtil.isNotEmpty(userInfoList)) {
			for (UserInfo userInfo : userInfoList) {
				Date vipExpireTime = userInfo.getVipExpireTime();
				if (vipExpireTime.before(date)) {
					userInfo.setIsVip(0);
					this.updateById(userInfo);
				}
			}
		}
	}

}
